package com.csc775.networking;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

import com.csc775.userinfo.UserInfo;

public class Server implements Runnable {
	private ServerSocket server = null;                     //ServerSocket object
	private Thread thread = null;                           //thread object
	private ServerThread client = null;                     //client object
	private int port = 2005;                                //port on which server is waiting for a client
	ObjectOutputStream out;                                 //Object output  stream for sending message
	ObjectInputStream in;                                   //Object Input stream for receiving message
	String message;                                         //message sent to server
	private DatagramSocket UDPSocket;                       //UDP socket
	private Socket TCPSocket;                               //TCP socket
	private static int ID;                                  //ID for user
	private static int clientCount = 10;                    //count for users
	public static int currClientcount = 0;                  //current user count
	public static String talkingUser = "";                  //current user who is talking

	// this the single copy of IP address of client and its UserInfo
	// object pointed. When a new client is connected the userinfo should
	// be added here.
	private static HashMap<String, UserInfo> clientInfo = new HashMap<String, UserInfo>();

	/**
	 * constructor for Server class
	 */
	public Server() {

		try {
			System.out.println("Server starting on port " + port
					+ ", please wait  ...");
			server = new ServerSocket(port);
			System.out.println("Server started: "
					+ InetAddress.getLocalHost().getHostAddress() + " "
					+ server.getLocalPort());
			start();
		} catch (IOException ioe) {
			System.out.println(ioe);
		}
	}

	/**
	 * static method to get the userInfo object
	 * @param ipAddr
	 * @return
	 */
	public static UserInfo getClientInfoObject(String ipAddr) {
		Iterator<UserInfo> itr = clientInfo.values().iterator();
		while (itr.hasNext()) {
			UserInfo temp = itr.next();
			if (temp.getUsername().equals(ipAddr))
				return temp;
		}
		return null;
	}

	@Override
	/**
	 * invoked after the server starts
	 */
	public void run() {
		while (thread != null) {
			try {
				while (true) {
					System.out.println("Waiting for a client ...");
					TCPSocket = server.accept();
					UDPSocket = new DatagramSocket(TCPSocket.getPort());
					System.out
							.println("Connected to "
									+ TCPSocket.getRemoteSocketAddress()
									+ " at port " + TCPSocket.getPort()
									+ " Socket info " + TCPSocket);
					if (currClientcount < clientCount) {
						currClientcount++;
						System.out.println("client count = " + currClientcount);
						addThread(TCPSocket, UDPSocket);
					}
				}
			} catch (IOException ie) {
				System.out.println("Acceptance Error: " + ie);
			}
		}
	}

	/**
	 * Adds new thread
	 * 
	 * @param TCPsocket
	 * @param UDPsocket
	 */
	public void addThread(Socket TCPsocket, DatagramSocket UDPsocket) {
		ID++; // assigns ID to each user
		// spawns a new thread to handle each client
		client = new ServerThread(this, TCPsocket, ID, UDPsocket);

		// start client thread
		client.start();

	}

	/**
	 * to get the user records
	 * 
	 * @return
	 */
	public static HashMap<String, UserInfo> getClientInfo() {
		return clientInfo;
	}

	/**
	 * add to the user records
	 * 
	 * @param u
	 */
	public static void addClientInfo(UserInfo u) {
		clientInfo.put(u.getUsername(), u);
	}

	/**
	 * to display the user records
	 */
	public static void displayClientRecords() {
		Iterator<UserInfo> iter = clientInfo.values().iterator();
		while (iter.hasNext()) {
			iter.next().print();
		}

	}

	/**
	 * Starts the thread
	 */
	public void start() {
		if (thread == null) {
			thread = new Thread(this);
			thread.start();

		}
	}

	/**
	 * Stops the thread
	 */
	public void stop() {
		if (thread != null) {
			thread.stop();
			thread = null;
		}
	}

	/**
	 * returns a set containing IP addresses of the users in the same group
	 * 
	 * @param forIP
	 * @param group
	 * @return
	 */
	public static Set<String> getIPHasGroup(String forIP, ArrayList group) {
		Set<String> ipList = new TreeSet<String>();
		Iterator<UserInfo> itr = clientInfo.values().iterator();

		while (itr.hasNext()) {
			UserInfo temp = itr.next();
			if (!temp.isActive())
				continue;
			String username = temp.getUsername();
			System.out.println(temp.getIP());
			for (int i = 0; i < group.size(); i++) {
				if (clientInfo.get(temp.getUsername()).getGrp()
						.contains(group.get(i))) {
					if (!username.equals(forIP)) {
						System.out.println("Matched IP ==>" + temp.getIP());
						ipList.add(temp.getIP());
					}
				}
			}
		}
		return ipList;

	}

	/**
	 * returns a set containing usernames of users in the same group
	 * 
	 * @param ips
	 * @return
	 */
	public static Set<String> getUserNameList(Set<String> ips) {
		Set<String> usernames = new TreeSet<String>();
		Iterator<UserInfo> itr = clientInfo.values().iterator();

		while (itr.hasNext()) {
			UserInfo temp = itr.next();
			String username = temp.getUsername();
			System.out.println(username);
			Iterator<String> iter2 = ips.iterator();
			while (iter2.hasNext()) {
				String ip = iter2.next();
				if (clientInfo.get(username).getIP().equals(ip)) {
					usernames.add(username);
				}
			}
		}
		return usernames;

	}

	/**
	 * main
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String args[]) {

		Server server = new Server();
	}
}